`include "../codigo/Alu.v"
`include "../codigo/Shifter.v"


module Execute (
    
    input       [0:0]   clock,
    input       [0:0]   reset,

    // Decode
	input       [0:0]   id_ex_selalushift,
    input       [0:0]   id_ex_selimregb,
    input       [0:0]   id_ex_selsarega,
    input       [2:0]   id_ex_aluop,
    input       [0:0]   id_ex_unsig, 
	input       [1:0]   id_ex_shiftop,
	input       [4:0]   id_ex_shiftamt,
	input      [31:0]   id_ex_rega,
	input       [2:0]   id_ex_msm,
	input       [2:0]   id_ex_msl,
	input       [0:0]   id_ex_readmem,
	input       [0:0]   id_ex_writemem,
	input       [0:0]   id_ex_mshw,
	input       [0:0]   id_ex_lshw,
	input      [31:0]   id_ex_regb,
	input      [31:0]   id_ex_imedext,
	input      [31:0]   id_ex_proximopc,
	input       [2:0]   id_ex_selwsource,
	input       [4:0]   id_ex_regdest,
	input       [0:0]   id_ex_writereg,
	input       [0:0]   id_ex_writeov,

	// Forwarding
	output     [31:0]   ex_fw_wbvalue,
	output      [0:0]   ex_fw_writereg,

	// Fetch
	output reg  [0:0]   ex_if_stall,

	// Memory
	output reg  [2:0]   ex_mem_msm,
	output reg  [2:0]   ex_mem_msl,
	output reg  [0:0]   ex_mem_readmem,
	output reg  [0:0]   ex_mem_writemem,
	output reg  [0:0]   ex_mem_mshw,
	output reg  [0:0]   ex_mem_lshw,
	output reg [31:0]   ex_mem_regb,
	output reg  [2:0]   ex_mem_selwsource,
	output reg  [4:0]   ex_mem_regdest,
	output reg  [0:0]   ex_mem_writereg,
	output reg [31:0]   ex_mem_aluout,
	output reg [31:0]   ex_mem_wbvalue

    );

    // ALU
    wire [31:0] aluout;
    wire [0:0] compout;
    wire [0:0] aluov;
	Alu ALU(
        .a(id_ex_rega),
        .b(mux_imregb),
        .op(id_ex_aluop),
        .aluout(aluout),
        .unsig(id_ex_unsig),
        .compout(compout),
        .overflow(aluov)
    );

    // Shifter 
    wire [31:0] result;    
	Shifter SHIFTER(
        .value_in(id_ex_regb),
        .shiftop(id_ex_shiftop),
        .shiftamt(mux_sarega),
        .result(result)
    );


    // --------------------------------------------------------------
    // Wire assignments

    // ALU / shifter output
    wire [31:0] mux_alusft;
    assign mux_alusft = (id_ex_selalushift) ? result : aluout;

    // Register A / shift amount
    wire [4:0] mux_sarega;
    assign mux_sarega = (id_ex_selsarega == 1'b1) ? 
        id_ex_shiftamt : id_ex_rega[4:0];
    
    // Register B / sign extended value
    wire [31:0] mux_imregb;
    assign mux_imregb = (id_ex_selimregb == 1'b1) ?
        id_ex_imedext : id_ex_regb;

    // Writeback
    wire [31:0] mux_wbvalue;
    assign mux_wbvalue = (id_ex_selwsource == 3'b000) ? mux_alusft :
        (id_ex_selwsource == 3'b010) ? id_ex_imedext :
        (id_ex_selwsource == 3'b011) ? id_ex_proximopc :
        (id_ex_selwsource == 3'b100) ? (32'b0 | compout) :
        32'bz;

    // Writeback value
    assign ex_fw_wbvalue = mux_wbvalue;
    assign ex_fw_writereg = id_ex_writereg;


    // --------------------------------------------------------------
    // Register assignments
    always @(negedge(clock)) begin

        // If under memory access read/write
        if (id_ex_readmem | id_ex_writemem) begin
            ex_if_stall <= 1'b1;
        end else begin
            ex_if_stall <= 1'bx;
        end

        // Values transferred to the memory unit
        ex_mem_msm <= id_ex_msm;
        ex_mem_msl <= id_ex_msl;
        ex_mem_readmem <= id_ex_readmem;
        ex_mem_writemem <= id_ex_writemem;
        ex_mem_mshw <= id_ex_mshw;
        ex_mem_lshw <= id_ex_lshw;
        ex_mem_regb <= id_ex_regb;
        ex_mem_selwsource <= id_ex_selwsource;
        ex_mem_regdest <= id_ex_regdest;
        ex_mem_writereg <= (!aluov | id_ex_writeov) & id_ex_writereg;
        ex_mem_aluout <= mux_alusft;
        ex_mem_wbvalue <= mux_wbvalue;

    end


    // Resets values
    always @(negedge(reset)) begin

        ex_mem_msm <= 3'b0;
        ex_mem_msl <= 3'b0;
        ex_mem_readmem <= 1'b0;
        ex_mem_writemem <= 1'b0;
        ex_mem_mshw <= 1'b0;
        ex_mem_lshw <= 1'b0;
        ex_mem_regb <= 32'b0;
        ex_mem_selwsource <= 3'b0;
        ex_mem_regdest <= 5'b0;  
        ex_mem_writereg <= 1'b0;
        ex_mem_aluout <= 32'b0;
        ex_mem_wbvalue <= 32'b0;

    end

endmodule
